#include "StdAfx.h"
#include "TextureManager.h"

#pragma warning(disable : 4996)

GLuint TextureManager::m_textureUnits[TEXTURE_UNITS_MAX];

void TextureManager::Bind(GLenum target, int unit, GLuint textureId)
{
	if (m_textureUnits[unit] != textureId)
	{
		glActiveTexture(GL_TEXTURE0 + unit);
		glBindTexture(target, textureId);
		m_textureUnits[unit] = textureId;
	}
}

TextureManager::TextureManager(void)
{
	m_muLoad = true;
}

TextureManager::~TextureManager(void)
{
	Clear();
}

void TextureManager::SetLoadDir(const char *path)
{
	m_loadDir = path;
	int len = strlen(path);
	char c = path[len - 1];
	if (!((c == '/') || (c == '\\'))) m_loadDir += '/';
}

Texture *TextureManager::GetTexture(const char *texName)
{
	auto it = m_textures.find(texName);
	if (it != m_textures.end()) return it->second;
	std::string fullPath = m_loadDir + texName;
	Texture *tex = new Texture;
	tex->name = texName;
	bool fok = LoadTexture(&tex->texture, fullPath.c_str(), &tex->sz);
	if (fok)
	{
		m_textures[texName] = tex;
	}
	else
	{
		delete tex;
		tex = 0;
	}
	return tex;
}

void TextureManager::Clear()
{
	for (auto &m : m_textures)
	{
		delete m.second;
	}
	m_textures.clear();
}

void TextureManager::DelTexture(char *texName)
{
	mapTexture::const_iterator it = m_textures.find(texName);
	if (it != m_textures.end())
	{
		delete it->second;
		m_textures.erase(it);
	}
}

void TextureManager::DelTexture(Texture *tex)
{
	delete tex;
	auto &end = m_textures.end();
	for (auto &it = m_textures.begin(); it != end; ++it)
	{
		if (it->second == tex)
		{
			m_textures.erase(it);
			break;
		}
	}
}

void SwapLineData(BYTE *data, int lineSize, int lineCount)
{
	BYTE *data2 = new BYTE[lineSize*lineCount];
	memcpy(data2, data, lineSize*lineCount);
	BYTE *p1 = data + lineSize*(lineCount - 1);
	BYTE *p2 = data2;
	for (int i = 0; i < lineCount; i++)
	{
		memcpy(p1, p2, lineSize);
		p1 -= lineSize;
		p2 += lineSize;
	}
}

bool TextureManager::LoadTexture(GLuint * texture, const char * path, SIZE *size)
{
	int w, h;
	FREE_IMAGE_FORMAT fif;
	FIBITMAP *img = FreeImage_Load_Mu(path);
	if (!img)
	{
		fif = FreeImage_GetFileType(path);
		if (fif != FIF_UNKNOWN)
		{
			img = FreeImage_Load(fif, path);
		}
		if (!img) return false;
	}
	w = FreeImage_GetWidth(img);
	h = FreeImage_GetHeight(img);
	if (size)
	{
		size->cx = w;
		size->cy = h;
	}
	FREE_IMAGE_COLOR_TYPE clrType = FreeImage_GetColorType(img);
	int bpp = FreeImage_GetBPP(img);
	BYTE *data = FreeImage_GetBits(img);
	GLenum dataType;
	switch (bpp)
	{
	case 8:
		dataType = GL_LUMINANCE;
		break;
	case 24:
		dataType = GL_BGR;
		break;
	case 32:
		dataType = GL_BGRA;
		break;
	default:
		FreeImage_Unload(img);
		return false;
	}
	int lineSize = FreeImage_GetPitch(img);
	SwapLineData(data, lineSize, h);
	if (*texture == 0) glGenTextures(1, texture);
	glBindTexture(GL_TEXTURE_2D, *texture);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
	glTexImage2D(GL_TEXTURE_2D, 0, clrType == FIC_RGBALPHA ? GL_RGBA : GL_RGB, w, h, 0, dataType, GL_UNSIGNED_BYTE, data);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	FreeImage_Unload(img);
	return true;
}

FIBITMAP *FreeImage_Load_Mu(const char *filename)
{
	int nskip = 4;
	FREE_IMAGE_FORMAT fif;
	char szPath[300];
	strcpy(szPath, filename);
	char *ext = strrchr(szPath, '.');
	strupr(ext);
	if (!strcmp(ext, ".OZJ"))
	{
		nskip = 24;
		fif = FIF_JPEG;
	}
	else if (!strcmp(ext, ".OZT"))
	{
		fif = FIF_TARGA;
	}
	else return nullptr;

	FILE *fp;
	fp = fopen(szPath, "rb");
	if (!fp) return nullptr;

	fseek(fp, 0, SEEK_END);
	long dataLen = ftell(fp) - nskip;
	fseek(fp, nskip, SEEK_SET);
	BYTE *data = new BYTE[dataLen];
	fread(data, dataLen, 1, fp);
	fclose(fp);
	FIMEMORY *mem = FreeImage_OpenMemory(data, dataLen);
	FIBITMAP *bmp = FreeImage_LoadFromMemory(fif, mem);
	FreeImage_CloseMemory(mem);
	delete[] data;
	return bmp;
}